package nl.unimaas.games.tron.player.mcts;

import java.io.Serializable;
import java.util.ArrayList;


@SuppressWarnings("serial")
public interface FinalNodeSelectionStrategy extends Serializable {
	public Node getNode(Node parent);
	
	public final static FinalNodeSelectionStrategy MAX_CHILD = new FinalNodeSelectionStrategy() {
		@Override
		public Node getNode(Node parent) {
			double bestValue = Double.NEGATIVE_INFINITY;
			Node bestNode = null;
			for (int i = 0; i < parent.children.size(); i++) {
				Node child = parent.children.get(i);
				if (child.value > bestValue) {
					bestValue = child.value / (child.visits + 1.0);
					bestNode = child;
				}
			}
			return bestNode;
		}
		
		@Override
		public String toString() {
			return "max";
		}
	};
	
	public final static FinalNodeSelectionStrategy SECURE_CHILD = new FinalNodeSelectionStrategy() {
		private double A = 1;
		
		@Override
		public Node getNode(Node parent) {
			float bestValue = Float.NEGATIVE_INFINITY;
			Node bestNode = null;
			for (int i = 0; i < parent.children.size(); i++) {
				Node child = parent.children.get(i);
				if (child.visits == 0)
					continue;
				float value = (float) (child.value / (child.visits + 1.0) + A / Math.sqrt(child.visits + 1.0));
				if (value > bestValue) {
					bestValue = value;
					bestNode = child;
				}
			}
			return bestNode;
		}
		
		@Override
		public String toString() {
			return "secure";
		}
	};
	
	public final static FinalNodeSelectionStrategy SECURE_SOLVED_CHILD = new FinalNodeSelectionStrategy() {
		private double A = 1;
		
		@Override
		public Node getNode(Node parent) {
			SolverNode bestNode = null;
			ArrayList<SolverNode> candidates = new ArrayList<SolverNode>(4);
			//check if there's a proven-win node
			int fastestWinSteps = Integer.MAX_VALUE;
			double bestPess = Double.NEGATIVE_INFINITY;
			SolverNode child;
			for (int i = 0; i < parent.children.size(); i++) {
				child = (SolverNode) parent.children.get(i);
				if (child.isProvenWin()) {
					int steps = child.getWinSteps();
					if (steps < fastestWinSteps) {
						fastestWinSteps = steps;
						bestNode = child;
					}
				}
				if (child.pess > bestPess)
					bestPess = child.pess;
			}
						
			if (bestNode != null)
				return bestNode;
			
			//remove inferior nodes
			for (int i = 0; i < parent.children.size(); i++) {
				child = (SolverNode) parent.children.get(i);
				if (child.opti > bestPess || child.opti >= 0)
					candidates.add(child);
				else {
					//System.err.println("Left out " + Board.moveToString(child.move) + "[" + child.pess + ", " + child.opti + "]");
				}
			}
			
			if (candidates.isEmpty()) {
				//System.err.println("No non-shitty moves available.");
				for (int i = 0; i < parent.children.size(); i++)
					candidates.add((SolverNode) parent.children.get(i));
			}
			
			double bestValue = Double.NEGATIVE_INFINITY;
			for (int i = 0; i < candidates.size(); i++) {
				child = candidates.get(i);
				double value = child.value / (child.visits + 1.0) + A / Math.sqrt(child.visits + 1.0);
				if (value > bestValue) {
					bestValue = value;
					bestNode = child;
				}
			}
				
			return bestNode;
		}
		
		@Override
		public String toString() {
			return "solved-secure";
		}
	};
	
	public final static FinalNodeSelectionStrategy ROBUST_CHILD = new FinalNodeSelectionStrategy() {
		@Override
		public Node getNode(Node parent) {
			float bestValue = Float.NEGATIVE_INFINITY;
			Node bestNode = null;
			for (int i = 0; i < parent.children.size(); i++) {
				Node child = parent.children.get(i);
				if (child.visits > bestValue) {
					bestValue = child.visits;
					bestNode = child;
				}
			}
			return bestNode;
		}
		
		@Override
		public String toString() {
			return "robust";
		}
	};
	
	public final static FinalNodeSelectionStrategy ROBUST_MAX_CHILD = new FinalNodeSelectionStrategy() {
		@Override
		public Node getNode(Node parent) {
			double bestValue = Double.NEGATIVE_INFINITY;
			Node bestNode = null;
			for (int i = 0; i < parent.children.size(); i++) {
				Node child = parent.children.get(i);
				double value = child.value / (child.visits + 1.0) + child.visits;
				if (value > bestValue) {
					bestValue = value;
					bestNode = child;
				}
			}
			return bestNode;
		}
		
		@Override
		public String toString() {
			return "robust-max";
		}
	};
}
